package ws

import (
	"fmt"
	"net/http"
	"sync"
	"time"

	"gitee.com/AlexWoo/arpc/alog"
	"gitee.com/AlexWoo/arpc/anet"
	"github.com/fasthttp/websocket"
)

func (c *Connection) connect(url string) error {
	alog.LogInfo(c.lctx, "WSClient Start connect [%p]", c)

	ws, resp, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != http.StatusSwitchingProtocols {
		return fmt.Errorf("http response %d", resp.StatusCode)
	}

	c.conn = ws
	c.remote = ws.RemoteAddr().String()
	c.local = ws.LocalAddr().String()

	alog.LogInfo(c.lctx, "WSClient connect to %s success", url)

	if c.onConnect != nil {
		c.onConnect(c)
	}

	c.loop()

	return nil
}

func WSConnect(conf *ClientConf, h anet.Handler, wg *sync.WaitGroup,
	onConnect anet.OnConnect, onDisconnect anet.OnDisconnect) *Connection {

	c := newConnection(&conf.Log)
	c.h = h
	c.isClient = true
	c.retryIntervalSec = conf.RetryIntervalMinSec
	c.onConnect = onConnect
	c.onDisconnect = onDisconnect

	wg.Add(1)
	go func(wg *sync.WaitGroup) {
		defer wg.Done()

		for {
			if c.closed == 1 {
				return
			}

			url := conf.URL
			alog.LogInfo(c.lctx, "WSClient start connect [%s]", url)
			err := c.connect(url)
			if err == nil {
				c.retryIntervalSec = conf.RetryIntervalMinSec
			} else {
				alog.LogError(c.lctx, "connect [%s] error[%v]", conf.URL, err)
			}

			if c.onDisconnect != nil {
				c.onDisconnect(c, err)
			}

			time.Sleep(time.Duration(c.retryIntervalSec) * time.Second)

			if err != nil {
				c.retryIntervalSec = 2 * c.retryIntervalSec
				if c.retryIntervalSec > conf.RetryIntervalMaxSec {
					c.retryIntervalSec = conf.RetryIntervalMaxSec
				}
			}
		}
	}(wg)

	return c
}

func Connect(addr string, conf *anet.Conf, h anet.Handler, wg *sync.WaitGroup,
	onConnect anet.OnConnect, onDisconnect anet.OnDisconnect) anet.Conn {

	cc := &ClientConf{
		URL:                 "ws://" + addr + conf.Path,
		Path:                conf.Path,
		RetryIntervalMinSec: conf.RetryIntervalMinSec,
		RetryIntervalMaxSec: conf.RetryIntervalMaxSec,
		Log:                 conf.Log,
	}

	return WSConnect(cc, h, wg, onConnect, onDisconnect)
}
